Sprite Byte: Collision Detection

Level: Beginner - Intermediate

by Alyce Watson [http://alycesrestaurant.com/]

Sprite Bytes are small tutorials that address a single method to be used with sprites in Liberty BASIC. See Sprite Bytes in previous newsletters for information on user-controled sprites, scrolling backgrounds and shooting. Please see the zipped archive of this newsletter for all files needed to run the demo program.

What are collisions?

Collisions happen when two sprites touch one another. If a missile hits a spaceship, that's a collision. If a hero touches a gold coin, that's a collision. The ability to detect collisions is central to the creation of arcade games, since this provides a way to keep score and to keep track of lives.

Changes in our demo game.

We've made some changes to the series of space arcade demos for this installment. Only one missile can be active at any time, but there are now five enemy ships. We can now detect missile strikes to the enemy ships, and we can also detect when the space ship has been hit by an enemy. When an enemy is hit, the score increases. When our ship is hit by an enemy, the number of lives decreases. When the number of lives hits zero, the game is over. There is a statusbar at the bottom to show the score and lives remaining.

Adding a statusbar.

A statictext control placed at the bottom of the window makes a fine statusbar. A "!font" command sets the font to be big and bold.

statictext #1.s, "     Score: 0     Lives: 5",0,maxY+51,maxX,30
'''
#1.s "!font verdana bold 14"

The color of the statictext that serves as a statusbar is set by issuing the following commands before the command to open the window.

BackgroundColor$="black"
ForegroundColor$="white"

Each time the score increases, or a life is lost, the statusbar is updated:

'update statusbar
#1.s "     Score: ";score;"     Lives: ";lives

Adding enemies.

A single enemy doesn't pose much of a challenge, so we've added five enemy ships. We'll create an array to hold the sprite names of the enemies, so that we can easily loop through them to move them.

    'add five enemies
    for i = 1 to 5
        #1.g "addsprite ";enemy$(i);" enemy"
    next

We don't want our enemies to all start at the same place, so we'll set their coordinates in a loop, modifying the x,y location for each one.

    for i = 1 to 5
        enemyY=enemyY-100
        enemyX=enemyX+60
        #1.g "spritexy ";enemy$(i);" ";enemyX;" ";enemyY
    next

Let's shoot the enemies!

To score points, we'll need to shoot the enemies with our missile, and detect when we've hit one. The collision detection routine is called each time through the main action loop by calling gosub [detectCollisions] .

[mainLoop]
    SCAN    'scan for user events
    gosub [moveship]
    gosub [moveEnemy]
    if activeShot then gosub [moveShot]
    gosub [detectCollisions]
    #1.g "drawsprites"    'update screen!
    if lives=0 then [quit]
    'slight pause to slow action:
    calldll #kernel32, "Sleep",10 as long, re as void
    goto [mainLoop]

Did we hit an enemy?

To detect collisions, we use the built-in spritecollides command. It looks like this:

#1.g "spritecollides spritename list$"

The spritename must be the actual name of the sprite against which collisions with other sprites are checked. The string variable will be filled with the names of all sprites that collided with the named sprite. It might look like this:

print list$
'shows:
badguy1 badguy2 missile1 tree

In our program, we can check with the INSTR() function to see if the word "enemy" appears in the list. If it does, the missile has hit an enemy. We must do this check, because a collision is also triggered when we fire the missile, because it touches our ship briefly.

    #1.g "spritecollides shot list$"
    if instr(list$,"enemy")>0 then

If an enemy has been hit, we do several things. We increase the score:

        score=score+1   'increment score

We remove the missile, and set the flag for an activeShot back to 0, thus enabling our ship to shoot again. When a shot is active, shooting is disabled.

        'remove missile
        #1.g "spritexy shot -100 -100"
        activeShot=0

We must also remove the enemy ship that has been hit. We start it again at the top of the screen in a random location.

        'start enemy at top, at random location:
        enemyX=int(rnd(0)*maxX)
        enemyY=0

We need to know which enemy was hit. The missile can hit our ship, or one of the five enemy ships. If the first five letters in list$ are "enemy" then the enemy ship spritename is the first word in the list. If they are not "enemy" then the first WORD$() is our ship spritename, so the second WORD$() contains the spritename of the enemy ship. We use either the first or second WORD$() as the spritename and issue a spritexy command to that sprite to place it in the predetermined location.

        'enemy ship spritename is either first or second word$ in list$
        if left$(list$,5)="enemy" then
            #1.g "spritexy ";word$(list$,1);" ";enemyX;" ";enemyY
        else
            #1.g "spritexy ";word$(list$,2);" ";enemyX;" ";enemyY
        end if

We then update the statusbar to reflect the new score, update the frame of animation with the drawsprites command, and play a WAV.

        'update statusbar
        #1.s "     Score: ";score;"     Lives: ";lives
        playwave "boom.wav",async
        #1.g "drawsprites"

Here is the collision detection routine for the missile:

    #1.g "spritecollides shot list$"
    if instr(list$,"enemy")>0 then
        score=score+1   'increment score
        'remove missile
        #1.g "spritexy shot -100 -100"
        activeShot=0
        'start enemy at top, at random location:
        enemyX=int(rnd(0)*maxX)
        enemyY=0
        'enemy ship spritename is either first or second word$ in list$
        if left$(list$,5)="enemy" then
            #1.g "spritexy ";word$(list$,1);" ";enemyX;" ";enemyY
        else
            #1.g "spritexy ";word$(list$,2);" ";enemyX;" ";enemyY
        end if
        'update statusbar
        #1.s "     Score: ";score;"     Lives: ";lives
        playwave "boom.wav",async
        #1.g "drawsprites"
    end if

Did we get hit by an enemy?

It wouldn't be much fun to keep shooting enemy ships indefinitely. We start with a set number of lives and each time an enemy ship hits our ship, we lose one. We use a method similar to the one that checked collisions with the missile. We use the spritecollides command to see if any other sprites collided with our ship.

    #1.g "spritecollides ship list$"
    if instr(list$,"enemy")>0 then

We don't care if the missile touches our ship, so we check to see if the word "enemy" is part of the list, using INSTR() If it is, we've been hit.

We must then decrement the number of lives:

        'enemy ship hit you
        lives=lives-1   'decrement lives

We then update the statusbar to reflect the new number of lives.

        'update statusbar
        #1.s "     Score: ";score;"     Lives: ";lives

We play a sound effect WAV.

        playwave "boom.wav",async

Just as we do when an enemy ship is hit by a missile, we remove it and start it at the top of the screen in a random location.

        'start enemy at top, at random location:
        enemyX=int(rnd(0)*maxX-80)+40
        enemyY=0
        'enemy ship spritename is first word$ in list$
        #1.g "spritexy ";word$(list$,1);" ";enemyX;" ";enemyY

Here is the routine to detect collisions between our ship and the enemy ship.

    #1.g "spritecollides ship list$"
    if instr(list$,"enemy")>0 then
        'enemy ship hit you
        lives=lives-1   'decrement lives
        'update statusbar
        #1.s "     Score: ";score;"     Lives: ";lives
        playwave "boom.wav",async
        'start enemy at top, at random location:
        enemyX=int(rnd(0)*maxX-80)+40
        enemyY=0
        'enemy ship spritename is first word$ in list$
        #1.g "spritexy ";word$(list$,1);" ";enemyX;" ";enemyY
    end if

That takes care of the collision detection for this game.

Ending the game.

When we have used all of our lives, the game is over. We do a check in our main loop for this:

    if lives=0 then [quit]

In the routine to end the program, we give the user a notice of his score.

    notice "Your score was ";score

Modify this game!

Feel free to modify this game and create something new and better. Some possible changes:


DEMO

The file sprite5.bas is included in the zipped archive of this newsletter. There are also bitmap files for the sprites and some wav files for the sound effects. Have fun!


Home

Tip Corner

API Corner

Qcard DLL #5

Sprite Byte

Projectile Motion

Sub Handlers

Agent Lesson

LB Server

A Web Presence

Website Review

An Interview

DoubleClick

Disk Cleaner

Newsletter help

Index